/*
 * Copyright (c) 2012 Sean Connelly
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//
// Pre-defined types based on patterns
//

var ratio = require('./ratio.js')

function ratio2pow(n)
{
	return ratio.createInt(2).pow(ratio.createInt(n));
}

function sub(r, n)
{
	return r.sub(ratio.createInt(n));
}

// types to use instead of `c_bit16b`, etc
var bitInstead = {
		// (v)ariable/(c)onstant
		// (u)nsigned/(s)igned
		// (b)ound/(c)ycle
		// bit size
		'v u c 8':  'byte',          'c u c 8':  'c_byte',
		'v u b 8':  'byteb',         'c u b 8':  'c_byteb',
		'v s c 8':  'sbytec',        'c s c 8':  'c_sbytec',
		'v s b 8':  'sbyte',         'c s b 8':  'c_sbyte',

		'v u c 16': 'ushort',        'c u c 16': 'c_ushort',
		'v u b 16': 'ushortb',       'c u b 16': 'c_ushortb',
		'v s c 16': 'shortc',        'c s c 16': 'c_shortc',
		'v s b 16': 'short',         'c s b 16': 'c_short',

		'v u c 32': 'uint',          'c u c 32': 'c_uint',
		'v u b 32': 'uintb',         'c u b 32': 'c_uintb',
		'v s c 32': 'intc',          'c s c 32': 'c_intc',
		'v s b 32': 'int',           'c s b 32': 'c_int',

		'v u c 64': 'ulong',         'c u c 64': 'c_ulong',
		'v u b 64': 'ulongb',        'c u b 64': 'c_ulongb',
		'v s c 64': 'longc',         'c s c 64': 'c_longc',
		'v s b 64': 'long',          'c s b 64': 'c_long'
	};

exports.add = function(addGenerator) {
		addGenerator('^((?:c_)?)(s?)bit([0-9]+)((?:b|c)?)$', function(match) {
				var is_constant = match[1] == 'c_';
				var is_signed = match[2] == 's';
				var is_bound = match[4] == 'b';
				var is_cycle = match[4] == 'c';

				if (is_signed)
				{
					if (is_bound)
						throw 'invalid type; use `' + match[1] + 'sbit' + match[3] + '`';
					is_bound = !is_cycle;
				}
				else
				{
					if (is_cycle)
						throw 'invalid type; use `' + match[1] + 'bit' + match[3] + '`';
					is_cycle = !is_bound;
				}
		
				// bit0 is invalid, bit0123 is invalid
				if (match[3].substr(0, 1) == '0')
					throw 'invalid bit size: ' + match[3];

				var bs = parseInt(match[3], 10);
				if (isNaN(bs))
					throw 'invalid bit size: ' + match[3];

				// check for better-named types for use instead... for example, instead
				// of using `ubit8`, the user should use `byte`
				var instead_key =
					(is_constant ? 'c ' : 'v ') + 
					(is_signed ? 's ' : 'u ') +
					(is_bound ? 'b ' : 'c ') +
					bs;
				if (typeof bitInstead[instead_key] !== 'undefined')
					throw 'use `' + bitInstead[instead_key] + '` instead';

				// arbitrary limit... just because
				if (config.maxTypedBitSize !== false && bs > config.maxTypedBitSize)
					throw 'bit size is too large (max size of ' + config.maxTypedBitSize + ' allowed)';

				var obj = {
						metatype: is_cycle ? 'numcycle' : 'numbound',
						constant: is_constant,
					};

				if (is_signed)
				{
					obj.numLow = ratio2pow(bs - 1).neg();
					obj.numHigh = sub(ratio2pow(bs - 1), 1);
				}
				else
				{
					obj.numLow = ratio.createInt(0);
					obj.numHigh = sub(ratio2pow(bs), 1);
				}

				return obj;
			});

		addGenerator('^(c_)?vec([0-9]+)$', function(match) {
				throw 'MYTYDO: implement vecN';
			});

		addGenerator('^(c_)?mat([0-9]+)$', function(match) {
				throw 'MYTODO: implement matN';
			});

		addGenerator('^(c_)?mat([0-9]+)x([0-9]+)$', function(match) {
				throw 'MYTODO: implement matNxM';
			});

		addGenerator('^(c_)?tensor([0-9]+)d([0-9]+)(x[0-9]+)*$', function(match) {
				throw 'MYTODO: implement tensorNdMxMx...';
			});
	};
